home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
src
/
stringmem.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-09
|
7KB
|
282 lines
/* stringmem.c -- Allocation of small pieces of memory
Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
This file is part of Jade.
Jade is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Jade is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jade; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "jade.h"
#include "jade_protos.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifdef NEED_MEMORY_H
# include <memory.h>
#endif
/* Initialise SM for the first time. */
int
sm_init(StrMem *sm)
{
int i;
for(i = 0; i < NUMBUCKETS; i++)
{
NewMList(&(sm->sm_MemBuckets[i].mbu_MemBlocks));
sm->sm_ChunksPerBlock[i] = MBLOCKSIZE / MCHNK_SIZE((i + 1) * GRAIN);
#ifdef STRMEM_STATS
sm->sm_AllocCount[i] = sm->sm_FreeCount[i] = 0;
#endif
}
sm->sm_MallocChain = NULL;
return(TRUE);
}
/* Release all memory allocated for SM, note that unless the `malloc-chain'
option is used malloc'd blocks aren't freed. The SM is left ready for
more allocations to be made. */
void
sm_kill(StrMem *sm)
{
int i;
for(i = 0; i < NUMBUCKETS; i++)
{
MemBlock *nxt, *mbl = (MemBlock *)sm->sm_MemBuckets[i].mbu_MemBlocks.mlh_Head;
while((nxt = (MemBlock *)mbl->mbl_Node.mln_Succ))
{
myfree(mbl);
mbl = nxt;
}
NewMList(&sm->sm_MemBuckets[i].mbu_MemBlocks);
sm->sm_MemBuckets[i].mbu_FreeList = NULL;
sm->sm_MemBuckets[i].mbu_FreeCount = 0;
#ifdef STRMEM_STATS
sm->sm_AllocCount[i] = sm->sm_FreeCount[i] = 0;
#endif
}
if(sm->sm_UseMallocChain)
{
MemChunk *mc = sm->sm_MallocChain;
while(mc)
{
MemChunk *nxtmc = mc->mc_Header.next;
myfree(mc);
mc = nxtmc;
}
sm->sm_MallocChain = NULL;
}
}
static int
new_memblock(StrMem *sm, MemBucket *mbu, int sizeIndex)
{
MemBlock *mbl;
int numchunks = sm->sm_ChunksPerBlock[sizeIndex];
int chnkbytes = (sizeIndex + 1) * GRAIN;
mbl = mymalloc(MBLK_SIZE(chnkbytes, numchunks));
if(mbl)
{
MemChunk *mc = mbl->mbl_Chunks;
int i, mcsiz = MCHNK_SIZE(chnkbytes);
AddMTail(&mbu->mbu_MemBlocks, &mbl->mbl_Node);
for(i = 0; i < (numchunks - 1); i++)
{
MemChunk *nxt = (MemChunk *)((char *)mc + mcsiz);
mc->mc_BlkType = MBT_FREE;
mc->mc_Mem.nextfree = nxt;
mc = nxt;
}
mc->mc_BlkType = MBT_FREE;
mc->mc_Mem.nextfree = mbu->mbu_FreeList;
mbu->mbu_FreeList = mbl->mbl_Chunks;
return(TRUE);
}
return(FALSE);
}
void *
sm_alloc(StrMem *sm, int size)
{
MemChunk *mc;
assert(size > 0);
if(size > MAXBUCKETSIZE)
{
mc = mymalloc(MCHNK_SIZE(size));
if(mc)
{
#ifdef STRMEM_STATS
sm->sm_AllocCount[NUMBUCKETS]++;
#endif
if(sm->sm_UseMallocChain)
{
mc->mc_Header.next = sm->sm_MallocChain;
sm->sm_MallocChain = mc;
}
else
mc->mc_BlkType = MBT_MALLOC;
}
else
return(NULL);
}
else
{
MemBucket *mbu;
int bucket = (size - 1) / GRAIN;
mbu = &sm->sm_MemBuckets[bucket];
if(!(mc = mbu->mbu_FreeList))
{
if(!new_memblock(sm, mbu, bucket))
return(NULL);
if(!(mc = mbu->mbu_FreeList))
return(NULL);
}
mc->mc_BlkType = bucket;
mbu->mbu_FreeList = mc->mc_Mem.nextfree;
#ifdef STRMEM_STATS
sm->sm_AllocCount[bucket]++;
#endif
}
return(mc->mc_Mem.mem);
}
u_char *
sm_strdupn(StrMem *sm, const u_char *old, int len)
{
char *new = sm_alloc(sm, len + 1);
if(new)
{
memcpy(new, old, len);
new[len] = 0;
}
return(new);
}
u_char *
sm_strdup(StrMem *sm, const u_char *str)
{
return(sm_strdupn(sm, str, strlen(str)));
}
/*
* This scans through all allocated MemBlocks in a MemBucket looking
* for any which totally consist of unused entrys, any found are
* released.
*/
static void
flush_bucket(StrMem *sm, int bucketIndex)
{
MemBlock *mbl = (MemBlock *)sm->sm_MemBuckets[bucketIndex].mbu_MemBlocks.mlh_Head;
MemBlock *nxt;
int chnksiz = MCHNK_SIZE((bucketIndex + 1) * GRAIN);
int numchnks = sm->sm_ChunksPerBlock[bucketIndex];
while((nxt = (MemBlock *)mbl->mbl_Node.mln_Succ))
{
MemChunk *mc = mbl->mbl_Chunks;
int j;
for(j = 0; j < numchnks; j++)
{
if(mc->mc_BlkType != MBT_FREE)
break;
mc = (MemChunk *)((char *)mc + chnksiz);
}
if(j == numchnks)
{
mc = mbl->mbl_Chunks;
for(j = 0; j < numchnks; j++)
{
MemChunk *last = NULL;
MemChunk *list = sm->sm_MemBuckets[bucketIndex].mbu_FreeList;
while(list)
{
if(list == mc)
{
if(last)
last->mc_Mem.nextfree = list->mc_Mem.nextfree;
else
sm->sm_MemBuckets[bucketIndex].mbu_FreeList = list->mc_Mem.nextfree;
break;
}
last = list;
list = list->mc_Mem.nextfree;
}
mc = (MemChunk *)((char *)mc + chnksiz);
}
RemoveM(&mbl->mbl_Node);
myfree(mbl);
}
mbl = nxt;
}
}
void
sm_free(StrMem *sm, void *mem)
{
if(mem)
{
MemChunk *mc = (MemChunk *)(((char *)mem) - sizeof(union mc_header));
int bucketnum = mc->mc_BlkType;
if(bucketnum == MBT_MALLOC)
{
myfree(mc);
#ifdef STRMEM_STATS
sm->sm_FreeCount[NUMBUCKETS]++;
#endif
}
else
{
MemBucket *mbu;
assert(bucketnum <= NUMBUCKETS);
mbu = &sm->sm_MemBuckets[bucketnum];
mc->mc_Mem.nextfree = mbu->mbu_FreeList;
mc->mc_BlkType = MBT_FREE;
mbu->mbu_FreeList = mc;
if(sm->sm_FreesBeforeFlush)
{
/* check whether to flush this bucket... */
if(++mbu->mbu_FreeCount >= (sm->sm_FreesBeforeFlush * sm->sm_ChunksPerBlock[bucketnum]))
{
mbu->mbu_FreeCount = 0;
flush_bucket(sm, bucketnum);
}
}
#if 0
if(sm == main_strmem)
{
/* Tell gc there's x bytes of data which may be able to be
reclaimed. This may seem backwards but it makes sense to gc
(and hence sm_flush()) when a lot of string-bytes have been
freed. */
data_after_gc += (bucketnum + 1) * GRAIN;
}
#endif
#ifdef STRMEM_STATS
sm->sm_FreeCount[bucketnum]++;
#endif
}
}
}
/*
* called when malloc() fails, tries to release any memory we can
*/
void
sm_flush(StrMem *sm)
{
int i;
for(i = 0; i < NUMBUCKETS; i++)
flush_bucket(sm, i);
}